package com.luxsoft.siipap.em.replica.online;

import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.time.StopWatch;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;

import com.luxsoft.siipap.em.replica.model.Registro;

/**
 * Administrador central de registros de replica 
 * Se utiliza para importar informacion a partir del sistema
 * de replica de SIIPAP
 * 
 * En realidad actua como un Facade y un Director. Como facade para
 * ser el punto central de acceso a los servicios de replica SIIPAP-SiipapEx
 * y como Director para coordinar el proceso de replica. Esta clase conoce
 * la logica del proceso y ocupa a los diversos colaboradores para ejecutar
 * en forma global la replicacion SIIPAP - SiipapEx.
 * 
 * Al ser administrado por Spring dentro de un contenedor, se presta a que
 * su tarea central el metodo execute sea programado como yuna tarea repetitiva
 * y constante.
 * 
 * @author Ruben Cancino 
 *
 */
public class ReplicationManager implements InitializingBean{
	
	private Logger logger=Logger.getLogger(getClass());
	
	private DateFormat df=new SimpleDateFormat("dd/MM/yyyy:hh:mm:ss");
	
	private ProveedorDeArchivos proveedorDeArchivos;
	private OrdenadorDeArchivos ordenador;
	private GeneradorDeRegistros generadorDeRegistros;
	private TransformadorARegistros transformadorARegistros;
	private EliminadorDeArchivos eliminadorDeArchivos;
	
	private List<ImportadorOnLine> importadores=new ArrayList<ImportadorOnLine>();
	
	/**
	 * Bound property para controlar si se deben eliminar los archivos .POR
	 * 
	 */
	private boolean eliminarArchivos=true;
	
	public ReplicationManager(){
		ordenador=new OrdenadorDeArchivos();
	}
	
	public void afterPropertiesSet() throws Exception {
		Assert.notNull(proveedorDeArchivos);
		Assert.notNull(transformadorARegistros);
		Assert.notNull(generadorDeRegistros);
		Assert.notNull(eliminadorDeArchivos);
		
	}
	
	/**
	 * Regresa una lista de los registros existentes para replicar
	 * 
	 * @return
	 */
	public List<Registro> buscarRegistros(){
		//Leer los archivos a procesar
		List<Registro> rows=new ArrayList<Registro>();
		File[] archivos=getProveedorDeArchivos().buscarArchivos();
		logger.info("Archivos leidos: "+archivos.length);		
		//Ordena los archivos TODO esta parte no tiene importacia por el momento
		List<File> sortedArchivos=ordenador.ordendar(archivos);
		
		for(File f:sortedArchivos){			
			try {
				String[] registros=generadorDeRegistros.procesar(f);
				for(String registro:registros){
					Registro r=getTransformadorARegistros().transformar(registro);
					r.setOrigen(f.getName());
					if(r!=null){
						rows.add(r);
					}
				}
			} catch (IOException e) {
				logger.error("Error al intentar genera Registro del archivo: "+f.getName()+e);
				Registro re=new Registro();
				re.setOrigen(f.getName());
				re.setAlias("ERROR");
				
			}
		}
		return rows;
	}
	
	/**
	 * Sincroniza el proceso de replica.
	 * 
	 * 	- Solicita los posibles archivos POR al proveedorDeArchivos
	 * 	- Los ordena (No hace nada por le momento)
	 *  - Solicita al generadroDeRegistros un set de registros para cada archivo
	 *  - Realiza alguna configuracion menor a los registros generados como asignarle el nombre del archivo del que provienen
	 *  - Procesa cada registro mandandolo al metodo process que a su vez lo pasa a cada uno de los importadores registrados
	 *     para su posible importacion a SiipapEx 
	 * 
	 * @return
	 */
	public void execute(){
		StopWatch watch=new StopWatch();
		watch.start();
		logger.info("Iniciando tarea de importacion "+df.format(new Date()));
		//Leer los archivos a procesar
		File[] archivos=getProveedorDeArchivos().buscarArchivos();		
		logger.info("\t Archivos a procesar : "+archivos.length);
		int row=0;
		//Ordena los archivos TODO esta parte no tiene importacia por el momento
		List<File> sortedArchivos=ordenador.ordendar(archivos);
		
		for(File f:sortedArchivos){
			//System.out.println("Procesando: "+f.getName());			
			//logger.info("Procesando: "+f.getName());
			try {
				String[] registros=generadorDeRegistros.procesar(f);
				for(String registro:registros){
					Registro r=getTransformadorARegistros().transformar(registro);
					r.setOrigen(f.getName());
					if(r!=null){
						procesar(r);
					}
				}
				disposeFile(f);
				row++;
			} catch (Exception e) {
				e.printStackTrace();
				String pattern="Error al intentar genera Registros a partir del archivo: {0} error: {1}";
				logger.error(MessageFormat.format(pattern, f.getName(),e.getMessage()));
				disposeOnError(f);
			}
			
		}
		watch.stop();
		logger.info("\t Archivos procesados: "+row+ "Segundos: "+watch.getTime());
	}
	
	/**
	 * Manda el registro leido a cada uno de los importadores
	 * Son estos los que deciden en forma particular si alguno de los registros les es
	 * util
	 * 
	 * @param r
	 * @throws Exception 
	 */
	public void procesar(Registro r) {
		if(logger.isDebugEnabled()){
			logger.debug("Procesando registro: "+r);
		}
		for(ImportadorOnLine imp:importadores){
			imp.importar(r);
		}	
	}
	
	/**
	 * Elimina el archivo de replica
	 * 
	 * @param file
	 * 
	 */
	protected  void disposeFile(final File file) {
		if(isEliminarArchivos())
			eliminadorDeArchivos.dispose(file);
	}
	
	/**
	 * Elimina el archivo de replica tomando particulares medidas
	 * cuando existio un error al leer/procesar el archivo
	 * 
	 * @param file
	 */
	public void disposeOnError(final File file){
		if(isEliminarArchivos()){
			try {
				eliminadorDeArchivos.disposeOnError(file);
			} catch (Exception e) {
				logger.error(e);
			}
		}
		
		
	}

	/**
	 * Proveedor de archivos responsable de buscar objetos de tipo {@link File} correspondientes
	 * a archivos de replica tipo .POR segun el sistema de replica de SIIPAP
	 * 
	 * @return
	 */
	public ProveedorDeArchivos getProveedorDeArchivos() {
		return proveedorDeArchivos;
	}
	public void setProveedorDeArchivos(ProveedorDeArchivos proveedorDeArchivos) {
		this.proveedorDeArchivos = proveedorDeArchivos;
	}
	
	/**
	 * Genera un String  a partir del contenido de  un archivo (File) de tipo .POR 
	 * 
	 * @return
	 */
	public GeneradorDeRegistros getGeneradorDeRegistros() {
		return generadorDeRegistros;
	}
	public void setGeneradorDeRegistros(GeneradorDeRegistros procesador) {
		this.generadorDeRegistros = procesador;
	}

	/**
	 * Transofmrador de Strings a Registros
	 * @return
	 */
	public TransformadorARegistros getTransformadorARegistros() {
		return transformadorARegistros;
	}

	public void setTransformadorARegistros(TransformadorARegistros transformer) {
		this.transformadorARegistros = transformer;
	}

	/**
	 * Strategia para eliminar archivos
	 * 
	 * @return
	 */
	public EliminadorDeArchivos getEliminadorDeArchivos() {
		return eliminadorDeArchivos;
	}
	public void setEliminadorDeArchivos(EliminadorDeArchivos eliminador) {
		this.eliminadorDeArchivos = eliminador;
	}
	

	/**
	 * Lista de importadores registrados para analizar y en su caso
	 * usar registros de archivos POR 
	 * 
	 * @return
	 */
	public List<ImportadorOnLine> getImportadores() {
		return importadores;
	}
	
	public void setImportadores(List<ImportadorOnLine> importadores) {
		this.importadores = importadores;
	}
	

	/**
	 * Si esta propiedad es verdadera los archivos procesados
	 * seran elininados despues de usarse
	 *  
	 * @return
	 */
	public boolean isEliminarArchivos() {
		return eliminarArchivos;
	}

	public void setEliminarArchivos(boolean eliminarArchivos) {
		this.eliminarArchivos = eliminarArchivos;
	}

	public static void main(String[] args) {
		ReplicationManager act=OnLineImportManager.getInstance().getReplicationManager();
		File dir=new File("C:\\PRUEBAS\\PORS");
		act.getProveedorDeArchivos().setReplicaDir(dir);
		act.setEliminarArchivos(false);
		act.execute();
		/*if(dir.exists() && dir.isDirectory()){
			act.getProveedorDeArchivos().setReplicaDir(dir);
			act.setEliminarArchivos(false);
			act.execute();
		}else
			System.out.println("No existe el direcorio de los pors"+dir.getAbsolutePath());
			*/
	}

}
